﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

using STSdb.Data;

namespace STSdb.MTL
{
    public class TablesCoordinator
    {       
        TransactionsCoordinator _transactionsCoordinator = null;

        public TablesCoordinator(TransactionsCoordinator transactionsCoordinator)
        {
            this._transactionsCoordinator = transactionsCoordinator;
        }


        #region "Handling Table Locators"

        Dictionary<string, object> _tables = new Dictionary<string, object>();
        ReaderWriterLockSlim _sync_tables = new ReaderWriterLockSlim();

        /// <summary>
        /// Returns TableLocators (Layer main XTable representation with two locators for reading and writing)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="T1"></typeparam>
        /// <param name="tableName"></param>
        /// <returns></returns>
        public TableLocators<T, T1> GetTable<T, T1>(string tableName)
        {
            object locators = null;

            _sync_tables.EnterUpgradeableReadLock();
            try
            {
                _tables.TryGetValue(tableName, out locators);

                if(locators != null)
                    return (TableLocators<T, T1>)locators;

                //if (_tables.ContainsKey(tableName))
                //    return (TableLocators<T, T1>)_tables[tableName];

                _sync_tables.EnterWriteLock();
                try
                {
                    _tables.TryGetValue(tableName, out locators);

                    if (locators != null)
                        return (TableLocators<T, T1>)locators;

                    //if (_tables.ContainsKey(tableName))
                    //    return (TableLocators<T, T1>)_tables[tableName];

                    //creating new table locator if necessary
                    TableLocators<T,T1> tableLocators = new TableLocators<T,T1>(this._transactionsCoordinator,tableName);

                    _tables.Add(tableName, tableLocators);

                    return tableLocators;
                }
                finally
                {
                    _sync_tables.ExitWriteLock();
                }
            }
            finally
            {
                _sync_tables.ExitUpgradeableReadLock();
            }

        }


        /// <summary>
        /// Returns Table as IReadLocator interface.
        /// Can return null, if such was not found.
        /// 
        /// Used by Tables Compactor, to the compacting moment Table Must have open read locator,
        /// because compactor is initialized by writing into table.
        /// </summary>
        /// <param name="tableName"></param>
        /// <returns></returns>
        public IReadLocator GetTableAsIReadLocator(string tableName)
        {
            object locators = null;



            _sync_tables.EnterReadLock();
            try
            {
                _tables.TryGetValue(tableName, out locators);

                if (locators != null)
                    return (IReadLocator)locators;

                return null;
               
            }
            finally
            {
                _sync_tables.ExitReadLock();
            }
        }

        /// <summary>
        /// Returns Table as IReadLocator interface.
        /// Can return null, if such was not found.
        /// 
        /// Used by Tables Compactor, to the compacting moment Table Must have open read locator,
        /// because compactor is initialized by writing into table.
        /// </summary>
        /// <param name="tableName"></param>
        /// <returns></returns>
        public IWriteLocator GetTableAsIWriteLocator(string tableName)
        {
            object locators = null;



            _sync_tables.EnterReadLock();
            try
            {
                _tables.TryGetValue(tableName, out locators);

                if (locators != null)
                    return (IWriteLocator)locators;

                return null;

            }
            finally
            {
                _sync_tables.ExitReadLock();
            }
        }

        /// <summary>
        /// Removes table from the TablesCoordinator Dictionary, 
        /// is called usually by Schema Coordinator DeleteTable.
        /// Never use it directly.
        /// Returns time when operation was completed, datetime is formed inside of the lock.
        /// </summary>
        /// <param name="tableName"></param>
        public DateTime DeleteTable(string tableName)
        {            
            _sync_tables.EnterWriteLock();
            try
            {
                object locators = null;

                _tables.TryGetValue(tableName, out locators);

                if (locators != null)
                {
                    //HERE WE MUST WAIT TILL WE HAVE NO READ LOCATORS OPEN, but compactor uses it inside of transaction, so for compactor must be special conditions
                    ///////////////////////////////////  HERE WOULD BE NICE TO CALL CLOSE OF WRITE LOCATOR, just to bring fileReferences to 0;
                    _tables.Remove(tableName);
                }
                                
                return DateTime.Now;
            }
            finally
            {
                _sync_tables.ExitWriteLock();
            }

        }

        #endregion


        #region "Tables Properties"

        
        Dictionary<string, object> _tablesProperties = new Dictionary<string, object>();
        ReaderWriterLockSlim _sync_tablesProperties = new ReaderWriterLockSlim();

        /// <summary>
        /// Saving into properties table
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="T1"></typeparam>
        /// <param name="tableName"></param>
        /// <param name="tableProperties"></param>
        public void SetTableProperties<T, T1>(string tableName, TableProperties<T, T1> tableProperties)
        {

            _sync_tablesProperties.EnterWriteLock();
            try
            {
                this._tablesProperties.Add(tableName, tableProperties);      
            }
            finally
            {
                _sync_tablesProperties.ExitWriteLock();
            }               

        }

        /// <summary>
        /// Getting table properties, saved with SetTableProperties.
        /// Can return NULL
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="T1"></typeparam>
        /// <param name="tableName"></param>
        /// <returns></returns>
        public TableProperties<T,T1> GetTableProperties<T,T1>(string tableName)
        {

            _sync_tablesProperties.EnterReadLock();
            try
            {
                object ret = null;
                _tablesProperties.TryGetValue(tableName,out ret);
                if(ret != null)
                    return (TableProperties<T,T1>)ret;
                else
                    return default(TableProperties<T,T1>);
                
            }
            finally
            {
                _sync_tablesProperties.ExitReadLock();
            }

        }

        /// <summary>
        /// Getting Table Properties as ITableProperties interface.
        /// Can return NULL
        /// </summary>
        /// <param name="tableName"></param>
        /// <returns></returns>
        public ITableProperties GetTableProperties(string tableName)
        {
            _sync_tablesProperties.EnterReadLock();
            try
            {
                object ret = null;
                _tablesProperties.TryGetValue(tableName, out ret);
                if (ret != null)
                    return (ITableProperties)ret;
                else
                    return null;

            }
            finally
            {
                _sync_tablesProperties.ExitReadLock();
            }
        }

        
        #endregion
    }
}
